package com.bwda.dsrs.base.jar.util.HotDeploy;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 自定义作用于sdk包热部署的类加载器
 *
 * @author yanzz
 * @date   2020-8-20
 */
public class MyHotSdkDeployClassLoader extends ClassLoader{



    private MyHotSdkDeployClassLoader(String jarPath, String [] packageNameArray){
        //重新加载该路径下的class资源
        reloadClass(jarPath, packageNameArray);
    }


    //创建建造者方法
    public static Builder builder(){

        return new Builder();
    }

    /**
     * 重新加载对象方法
     *
     *
     * @param jarPath           jar包的绝对路径
     * @param packageNameArray  要将那些包下的class文件进行热部署 传入过来的包路径用/分割
     */
    private void reloadClass(String jarPath, String [] packageNameArray){
        File jarFile = new File(jarPath);
        try{
            //实例化一个JarFile对象便于对jar包中类进行操作
            JarFile jar = new JarFile(jarFile);
            Enumeration entries = jar.entries();
            //循环该包下的文件
            while (entries.hasMoreElements()){

                JarEntry entry = (JarEntry)entries.nextElement();

                String name = entry.getName();

                if(name.charAt(0) == '/'){
                    name = name.substring(1);
                }

                for(String packageName : packageNameArray){
                    //如果找到目标包名
                    if (name.startsWith(packageName)){
                        int idx = name.lastIndexOf('/');

                        if (idx != -1){
                            packageName = name.substring(0, idx).replace('/', '.');
                        }

                        //如果这个以class为结尾
                        if ((name.endsWith(".class")) && (!entry.isDirectory())){
                            //去掉 .class
                            String className = name.substring(packageName.length() + 1, name.length() - 6);
                            //实例化
                            File classFile = new File(packageName + '.' + className);
                            //将这个JarEntry对象转成输入流
                            InputStream is = jar.getInputStream(entry);

                            ByteArrayOutputStream baos = new ByteArrayOutputStream();

                            //这边非加密jar 的方式
                            int bufferSize = 4096;
                            byte[] buffer = new byte[bufferSize];
                            int bytesNumRead = 0;
                            while ((bytesNumRead = is.read(buffer)) != -1) {
                                baos.write(buffer, 0, bytesNumRead);
                            }

                            /*
                            //这边是如果是加密的class（取反 因为加密方式用的是取反）文件就这么取出

                            int temp =-1;
                            while((temp=is.read())!=-1){
                                baos.write(temp^0xff);//读出的数据进行取反；
                            }*/
                            byte[] classBytes = baos.toByteArray();
                            this.instanceNewClass(classFile.getName(), is, classBytes);
                        }
                    }
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 重新实例化Class对象
     * @param className
     * @param is
     * @param classBytes
     */
    private void instanceNewClass(String className, InputStream is, byte[] classBytes){
        try {
            is.read(classBytes);
            defineClass(className, classBytes, 0, classBytes.length);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }







    //内部建造者类
    public static class  Builder{

        private  String jarPath;

        private  String [] packageNameArray;

        public Builder jarPath(String jarPath)
        {
            this.jarPath=jarPath;
            return  this;
        }
        public Builder packageNameArray(String [] packageNameArray)
        {
            this.packageNameArray=packageNameArray;
            return  this;
        }

        public MyHotSdkDeployClassLoader build()
        {
            return new MyHotSdkDeployClassLoader(this.jarPath, this.packageNameArray);
        }
    }

}
